
"""
#利用鸢尾花数据集画出P-R曲线
"""
print(__doc__) # 打印注释
# 导入包和模块
import matplotlib.pyplot as plt # matplotlib 绘图
import numpy as np # numpy 数值计算
from sklearn import svm, datasets # 导入SVM 支持向量机算法来做分类 datasets 是数据集模块
from sklearn.metrics import precision_recall_curve # 导入精确率-召回率评价曲线模块
from sklearn.metrics import average_precision_score # 导入平均精确率模块
from sklearn.preprocessing import label_binarize # 标签的二值化处理； 鸢尾花数据集标签有三类，需要将其转化为两类
from sklearn.multiclass import OneVsRestClassifier # 多类处理的方法采用 OneVSRest：一对其余 多类转两类
# 构建 一个类作为正类 剩下作为负类 的 分类器
# 导入训练集合测试集模块
from sklearn.model_selection import train_test_split # 适用于anaconda 3.7
#加载 iris 鸢尾花数据集
iris = datasets.load_iris()
X = iris.data # 150 * 4 有四组特征 每组150个 的矩阵
y = iris.target # 150 * 1 类别标签
# 标签二值化的前提
# 鸢尾花有三个类别
# 将三个类转为001, 010, 100的格式 指代三种类别
y = label_binarize(y, classes=[0, 1, 2])

# OneVsRestClassifier策略转为二类分类问题
n_classes = y.shape[1]
# print(n_classes) #观察形状 有三列

# print (y) # 此时 y 为 150 * 3 的一个矩阵

# 增加800维的噪声特征
# 增加分类的难度 提高分类模型的泛化能力

random_state = np.random.RandomState(0) # 增加噪声通过使用随机数

# RandomState 随机状态 可以使得重新运行程序时，得到相同的随机数，不随时间更改
# 否则随机数的产生随时间改变噪声 算法模型在训练集和测试集上的效果会有差异

n_samples, n_features = X.shape  # n_samples：150, n_features：4
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)] # np.c_ column 增加200*4 八百个列（维度）的噪声

# 将数据拆分成训练集和测试集 比例为0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5, random_state=random_state) #随机数，填0或不填，每次都会不一样

# 运行SVM分类器 使用一对其余分类器
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True, random_state=random_state))

# 把训练集放进fit函数进行拟合训练 
# 将X_test测试集放到拟合函数中测试得到 y_score 得到预测值的预测分数
y_score = classifier.fit(X_train, y_train).decision_function(X_test)
# print(y_score)

# 将预测值和真实值进行比较
# 评价测试的性能通过绘制准确性和召回率的PR曲线 Precision-Recall plot

# 建立三个字典
precision = dict() # 精确率

recall = dict() # 召回率

average_precision = dict() # 平均精确率

# i= 0，1，2 对应三类鸢尾花
for i in range(n_classes):
        precision[i], recall[i], _ = precision_recall_curve(y_test[:, i],  y_score[:, i]) 
        # 下划线是返回的阈值。作为一个名称：此时“_”作为临时性的名称使用。
        average_precision[i] = average_precision_score(y_test[:, i], y_score[:, i]) 
        #[: , i]切片取某一列的所有行，y_score[: , i]第i个类的分类结果性能

# print(precision.values(), recall.values())
# 计算微平均值 
# 微平均是区别于宏平均 是对三个分类类别的分类性能求平均

precision["micro"], recall["micro"], _ = precision_recall_curve(y_test.ravel(),  y_score.ravel())
# ['micro'] 算精确率的微平均值 ； ravel()将多维数组降为一维 平面化处理
average_precision["micro"] = average_precision_score(y_test, y_score, average="micro")

print(precision["micro"],recall["micro"])

for i in range(n_classes):
    print(recall[i],precision[i])

# 绘制PR曲线
plt.clf()#clf 函数用于清除当前图像窗口
# 对每一类分类性能绘图
plt.plot(recall["micro"], precision["micro"],

         label='micro-average Precision-recall curve (area = {0:0.2f})'.format(average_precision["micro"]))

for i in range(n_classes): # 打印三个类的PR曲线

    plt.plot(recall[i], precision[i],

             label='Precision-recall curve of class {0} (area = {1:0.2f})'.format(i, average_precision[i]))
    
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05]) #xlim、ylim：分别设置X、Y轴的显示范围。 y = 1.05 使得y轴清晰一些 如果 y =1 观察起来不方便
plt.xlabel('Recall', fontsize=16)
plt.ylabel('Precision',fontsize=16) # x和y轴标签分别为Recall和Precision
plt.title('Extension of Precision-Recall curve to multi-class',fontsize=16)
plt.legend(loc="lower right")#legend 是用于设置图例的函数 loc 位置
plt.show()#展示PR曲线





